package com.gc.cscott.store;

import com.gc.cscott.domain.contest.TestCase;
import com.gc.cscott.domain.Competitor;
import com.gc.cscott.domain.contest.Contest;
import com.gc.cscott.domain.result.CompetitorResult;
import com.gc.cscott.domain.result.TestCaseResult;
import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.inject.Inject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

/**
 *
 * @author root
 */
public class ResultsStorage {

    public enum WriteMode {

        Append(false),
        Owerwrite(true);
        final boolean canOverwrite;

        private WriteMode(boolean canOverwrite) {
            this.canOverwrite = canOverwrite;
        }

        public boolean isCanOverwrite() {
            return canOverwrite;
        }

        public boolean isSkipTestWhenPresended() {
            return !isCanOverwrite();
        }
    }
    public static final String RESULT_JSON = "result.json";
    private static final String timeMark = "today";//CscottUtil.currentTimeString();
    @Inject
    Contest contest;
    Gson gson = new GsonBuilder().serializeNulls().create();

    public File storeReport(Competitor competitor, String reportHtml, String fileNameExt) throws IOException {
        File reportFile = getCompetitorReportFile(competitor, fileNameExt);
        FileUtils.writeStringToFile(reportFile, reportHtml, "UTF-8");
        return reportFile;
    }

    private File getCompetitorReportFile(Competitor competitor, String fileNameExt) throws IOException {
        return new File(getCompetitorResultsDir(competitor, true), competitor.getName().replaceAll("[^A-Za-z0-9]", "_")+"_report."+fileNameExt);
    }

    private File getCompetitorResultsDir(Competitor competitor, final boolean createIfMissed) throws IOException {
        File resultDir = new File(contest.getResultDir());
        if (resultDir.isDirectory() == false) {
            if (resultDir.mkdirs() == false) {
                throw new FileNotFoundException("Results directoty is missed: " + resultDir.getAbsolutePath());
            }
        }
        File competitorResultDir = new File(resultDir, competitor.getName());
        if (competitorResultDir.exists() == false) {
            if (createIfMissed == true) {
                if (competitorResultDir.mkdir() == false) {
                    throw new IOException("Failed to created competitor's results store: " + competitorResultDir.getAbsolutePath());
                }
            } else {
                return null;
            }
        }
        if (competitorResultDir.isDirectory() == false) {
            throw new IOException("File in place of competitor's results store: " + competitorResultDir.getAbsolutePath());
        }
        return competitorResultDir;
    }

    public boolean hasResults(Competitor competitor, TestCase testCase) throws IOException {
        CompetitorResult competitorResult = loadResults(competitor);
        return competitorResult.getResult(testCase) != null;
    }

    public void storeResult(Competitor competitor, TestCaseResult testCaseResult, WriteMode writeMode) throws IOException {
        File competitorResultDir = Preconditions.checkNotNull(getCompetitorResultsDir(competitor, true));
        File resultFile = new File(competitorResultDir, RESULT_JSON);
        final CompetitorResult updatedResults;
        if (resultFile.exists() == true) {
            String currentJson = FileUtils.readFileToString(resultFile, "UTF-8");
            CompetitorResult currentResults = gson.fromJson(currentJson, CompetitorResult.class);
            if (timeMark.equals(currentResults.getTimeMark()) == true) { //update.
                updatedResults = currentResults;
                TestCaseResult correspondigResult = updatedResults.getResult(testCaseResult.getTaskNumber(), testCaseResult.getTestCaseNumber());
                if (correspondigResult != null) {
                    if (writeMode.isCanOverwrite() == true) {
                        if (updatedResults.getResults().remove(correspondigResult) == false) {
                            throw new IllegalStateException("Error in code, failed to remove value from collection. But it MUST NOT FAIL.");
                        }
                        updatedResults.addResult(testCaseResult);
                    } else {
                        throw new IOException("Can not owerwrite task " + testCaseResult.getTaskNumber().intValue() +
                                " test " + testCaseResult.getTestCaseNumber().intValue() + " result value for: " + competitor.getName() + ". Use " + WriteMode.Owerwrite + " to do so.");
                    }
                } else {
                    updatedResults.addResult(testCaseResult);
                }
            } else { //overwrite.
                if (true) {//temporary stub.
                    throw new IllegalStateException("Not yet imlemented");
                }
                File backupResultFile = new File(resultFile.getParentFile(), "result-" + currentResults.getTimeMark() + ".json");
                if (resultFile.renameTo(backupResultFile) == false) {
                    throw new IOException("Failed to rename " + resultFile.getAbsolutePath() + " to " + backupResultFile.getAbsolutePath());
                }
                updatedResults = new CompetitorResult();
                updatedResults.setCompetitorName(competitor.getName());
                updatedResults.setTimeMark(timeMark);
                updatedResults.addResult(testCaseResult);
            }

        } else {
            updatedResults = new CompetitorResult();
            updatedResults.setCompetitorName(competitor.getName());
            updatedResults.setTimeMark(timeMark);
            updatedResults.addResult(testCaseResult);
        }
        for(TestCaseResult cleanUp:updatedResults.getResults()){
            cleanUp.setHint(StringUtils.substring(""+cleanUp.getHint(), 0, 1024));
        }
        String json = gson.toJson(updatedResults);
        FileUtils.writeStringToFile(resultFile, json, "UTF-8");
    }

    public CompetitorResult loadResults(Competitor competitor) throws IOException {
        File resultsDir = getCompetitorResultsDir(competitor, false);
        if (resultsDir == null) {
            return new CompetitorResult(competitor);
        } else {
            File resultsFile = new File(resultsDir, RESULT_JSON);
            if (resultsFile.isFile() == true) {
                String json = FileUtils.readFileToString(resultsFile, "UTF-8");
                return gson.fromJson(json, CompetitorResult.class);
            } else {
                return new CompetitorResult(competitor);
            }
        }
    }

    public String getOutputStoreFileName(TestCase testCase) {
        return String.format("output-%s-T%02d-t%02d.txt",
                timeMark,
                testCase.getTask().getNumber().intValue(),
                testCase.getNumber().intValue());
    }

    public String storeOutput(Competitor competitor, TestCase testCase, String outputText, WriteMode writeMode) throws IOException {
        File outDir = new File(getCompetitorResultsDir(competitor, true), "out");
        File outFile = new File(outDir, getOutputStoreFileName(testCase));
        if (outFile.isFile() == true) {
            if (writeMode.isCanOverwrite() == false) {
                throw new IOException("Can not overwrite file (use " + WriteMode.Owerwrite + " mode to do so): " + outFile.getAbsolutePath());
            }
        }
        FileUtils.writeStringToFile(outFile, outputText, "ASCII");
        return outFile.getName();
    }
}
